/* Quick intro, using this macro as an example:
 *
 * assign_expr_var (token LHS, token RHS)
 *    where LHS.st_entry_not_required
 *    where LHS.is_uninitialized
 * @@@
 *   zval** p_lhs = &local_$LHS;
 *   \read_rvalue ("rhs", RHS);
 *   \write_var ("p_lhs", "rhs", LHS, RHS);
 * @@@
 *
 * Macros are defined by their name, a list of argument, a list of rules, and a
 * body. Generate_C will attempt to 'instantiate' a macro with its name and
 * some arguments. Each macro of that name will be matched against in the order
 * it appears. When all of pattern's rules match the parameters, that macro
 * will be evaluated. If no macro matches, and error is given.
 *
 * Matching:
 *    Before we look at the rules, we need to look at the signature. The
 *    signature is a list of parameters, in capital letters (LHS and RHS), and
 *    types (token in both cases).
 *
 *    A rule is a line starting with 'where', and followed by a boolean expression. There are only two kinds of boolean expr:
 *	 Lookup: Parameters which are 'node's (ie correspond to an MIR::Node*)
 *	 can have their attrs looked up. If they have do not have the
 *	 attribute, or they have a Boolean false stored, the lookup fails.
 *	 Otherwise it succeeds.
 *
 *	 Equals: In the form 'LHS == RHS' - this does a string comparison
 *	 between the parameters, where they are coerced to strings. Coersions
 *	 will be explained in the type section later.
 *
 *	 The operands of an Equals are expressions: Parameters, strings, calls
 *	 to other macros or callbacks. Macro calls and callbacks are explained
 *	 later.
 *
 * Types:
 *    There are only 3 types, whose name must be used in a type signature:
 *       node: Can have attributes lookup up. Evaluating it is a evaluation-time error.
 *
 *       string: Represents a String* (or a quoted string when created in 'user code').
 *
 *	 token: A token is an MIR::Identifier*. Attributes can be looked up, or
 *	 it can be evaluated itself, which coerces it to its 'value' string.
 *	 Tokens can be used as either strings or nodes.
 *
 *    Evaluating a lookup can also produce a boolean, but these are always
 *    coerced to either "TRUE" or "FALSE".
 *
 *
 * Bodies:
 *    The body is simply a C string, interspersed with interpolations, macro calls and callbacks. It starts and
 *    ends with @@@. The evalutation of a macro is simply a string of its
 *    contents, with appropriate strings substituted in for interpolations etc.
 *    Whitespace is preserved.
 *
 *    Bodies allow two kinds of interpolations:
 *      $LHS: coerces LHS into a string (which works for tokens and strings,
 *      and leads to evalutation-time error for nodes).
 *
 *       ${LHS.attr_name}: the same as a lookup.
 *
 * Macro calls:
 *    Rules and bodies both support macro calls. In the example above,
 *    \read_value (...) is  macro call. Parameters can be nodes, tokens, or
 *    strings. Strings are written as quoted strings: "rhs". Nodes and tokens
 *    must, of course, be passed using their name in the caller.
 *
 * Callbacks:
 *    Callbacks are the same as macros, except they call a c++ function, which
 *    must be registed in Generate_C. They are called using:
*     \cb:call_back_name (PARAM). For now, only 1 parameter is allowed, but its
*     easy to fix that.
 *
 * Comments:
 *    C or C++ comments, are allowed outside bodies. Comments inside bodies and
 *    will appear in the generated code.
 *
 *  Note: I may be wrong about some of the evaluation-time errors for node
 *  coersions. If you see some stray "TRUE"s in your code, this may be the
 *  problem.
 *
 * TODO: we use ZEND_FETCH_CLASS_DEFAULT to get class definitions for static
 * methods and attributes. Not sure that is correct.
 *
 * TODO: we might be able to cache zend_class_entry info for static vars,
 * calls to "new", etc.
 */

/*
 * Simple assignments
 * $x = $y;
 */

assign_expr_var (token LHS, node RHS)
   where LHS.st_entry_not_required
   where LHS.is_uninitialized
@@@
  PCH::assign_expr_var ($LHS,$RHS);
@@@

assign_expr_var (token LHS, node RHS)
@@@
PHC::assign_expr_var ($LHS,$RHS);
@@@

/*
 * $x =& $y;
 */
assign_expr_ref_var (token LHS, token RHS)
@@@
   PHC::assign_expr_ref_var ($LHS, $RHS);
@@@

/*
 * Casts
 */
assign_expr_cast (token LHS, token RHS, string TYPE)
@@@
PHC::assign_expr_cast<$TYPE> ($LHS, $RHS,$TYPE);
@@@

cast_var (string LHS, string TYPE)
@@@
PCH::cast_var<$TYPE> ($LHS,$TYPE);
@@@

convert_to (string TYPE) where TYPE == "IS_ARRAY" @@@convert_to_array@@@
convert_to (string TYPE) where TYPE == "IS_BOOL" @@@convert_to_boolean@@@
convert_to (string TYPE) where TYPE == "IS_DOUBLE" @@@convert_to_double@@@
convert_to (string TYPE) where TYPE == "IS_LONG" @@@convert_to_long@@@
convert_to (string TYPE) where TYPE == "IS_NULL" @@@convert_to_null@@@
convert_to (string TYPE) where TYPE == "IS_STRING" @@@convert_to_string@@@
convert_to (string TYPE) where TYPE == "IS_OBJECT" @@@convert_to_object@@@

/*
 * Bin-ops
 */

// We could do this for non-LOCAL, but we'd only be saving an refcount++ and a refcount--.
assign_expr_bin_op (token LHS, node LEFT, node RIGHT, string OP_FN)
   where LHS.st_entry_not_required
   where LHS.is_uninitialized
@@@
  PHC::assign_expr_bin_op<$OP_FN> ($LHS, $LEFT, $RIGHT, $OP_FN);
@@@

// OP_FN: for example "add_function"
// The caller must sort out the order of LEFT and RIGHT for > and >=
// We use NODE for LEFT and RIGHT, since they might be literals
assign_expr_bin_op (token LHS, node LEFT, node RIGHT, string OP_FN)
@@@
PCH::assign_expr_bin_op<$OP_FN>(LHS,LEFT,RIGHT,$OP_FN);
@@@

/*
 * Unary-ops
 */
assign_expr_unary_op (token LHS, node RHS, string OP_FN)
@@@
PHC::assign_expr_unary_op<$OP_FN>(LHS, RHS, $OP_FN);
@@@

/*
 * Pre-op
 */
pre_op (token VAR, string OP_FN)
@@@
PHC::pre_op<$OP_FN> ($VAR,$OP_FN);
@@@

/*
 * Return
 */

return (token RETVAL, node RET)
   where RET.return_by_ref
@@@
  PHC::Return($RETVAL,$RET);
@@@

// Return-by-value
return (token RETVAL, node RET)
@@@
  PHC::Return ($RETVAL,RET);
@@@


/*
 * Branch
 */
branch (token COND, string TRUE_TARGET, string FALSE_TARGET)
@@@
   PCH::branch ($COND, $TRUE_TARGET,  $FALSE_TARGET);
@@@

   

/*
 * Var-vars
 */
assign_expr_var_var (token LHS, token INDEX)
@@@
PHC::assign_expr_var_var ($LHS, $INDEX);
@@@

assign_expr_ref_var_var (token LHS, token INDEX)
@@@
PHC::assign_expr_ref_var_var ($LHS,$INDEX);
@@@

/*
 * Array access
 */
assign_expr_array_access (token LHS, token ARRAY, token INDEX)
@@@
PHC::assign_expr_array_access ($LHS,$ARRAY,$INDEX);
@@@

assign_expr_ref_array_access (token LHS, token ARRAY, token INDEX)
@@@
PHC::assign_expr_ref_array_access ($LHS,$ARRAY,$INDEX);
@@@

/*
 * Constants
 */

assign_expr_constant_body (token LHS, string CONSTANT)
   where LHS.use_non_ref_version
@@@
PHC::assign_expr_constant_body ($LHS, $CONSTANT);
@@@

assign_expr_constant_body (token LHS, string CONSTANT)
   where LHS.use_ref_version
@@@
PHC::assign_expr_constant_body ($LHS,$CONSTANT);
@@@

assign_expr_constant (token LHS, string CONSTANT)
@@@
PHC::assign_expr_constant ( $LHS, $CONSTANT);
@@@

get_st_entry (string SCOPE, string ZVP, token VAR)
   where SCOPE == "LOCAL"
   where VAR.st_entry_not_required
@@@
  PHC::get_st_entry ($SCOPE, $ZVP, $VAR);
@@@

// TODO: inline better
get_st_entry (string SCOPE, string ZVP, token VAR)
@@@
PHC::get_st_entry ($SCOPE, $ZVP,  $VAR);
@@@

scope (string SCOPE) where SCOPE == "LOCAL" @@@EG(active_symbol_table)@@@
scope (string SCOPE) where SCOPE == "GLOBAL" @@@&EG(symbol_table)@@@

/*
 * Assign from Literal - it seems like we could use assign_expr_var, and
 * rvalue, but we always want a new value for LHS, and read_rvalue generates a
 * zval, not a zval* (except when optimized, but we cant guarantee that).
 */
assign_expr_literal (token LHS, node RHS)
@@@
  PHC::assign_expr_literal ($LHS, RHS);
@@@


/*
 * read_value
 */

// For literals
read_rvalue (string ZVP, node LIT)
   where \cb:is_literal(LIT) == "TRUE"
   where LIT.pool_name
@@@
  PHC::read_rvalue1 ($ZVP, $LIT);
@@@

read_rvalue (string ZVP, node LIT)
   where \cb:is_literal(LIT) == "TRUE"
@@@
  PHC::read_rvalue2 ($ZVP, $LIT);
@@@

// Not for literals (not that the signature changes here -- that's intentional
// -- the rules are checked before the signature is)
read_rvalue (string ZVP, token VAR)
   where VAR.st_entry_not_required
   where VAR.is_uninitialized
@@@
  PHC::read_rvalue3 ($ZVP, $VAR);
@@@

read_rvalue (string ZVP, token VAR)
   where VAR.st_entry_not_required
   where VAR.is_initialized
@@@
  PHC::read_rvalue4 ($ZVP, $VAR);
@@@

read_rvalue (string ZVP, token VAR)
   where VAR.st_entry_not_required
@@@
  PHC::read_rvalue5 ($ZVP, $VAR);
@@@

read_rvalue (string ZVP, token TVAR)
@@@
  PHC::read_rvalue6 ($ZVP, $TVAR);
@@@


/*
 * write_var
 */

write_var (string LHS, string RHS, node TLHS, node TRHS)
   where TLHS.is_uninitialized
@@@
     PHC::write_var ($LHS,$RHS, $TLHS,$TRHS);
@@@

write_var (string LHS, string RHS, node TLHS, node TRHS)
@@@
     PHC::write_var ($LHS, $RHS, $TLHS,$TRHS);
@@@

write_var_inner (string LHS, string RHS, node TLHS, node TRHS)
   where TRHS.is_uninitialized
@@@
     PHC::write_var ($LHS, $RHS,$TLHS,  $TRHS);
@@@


write_var_inner (string LHS, string RHS, node TLHS, node TRHS)
@@@
     PHC::write_var ($LHS, $RHS, $TLHS,  $TRHS);
@@@

/*
 * Var-vars
 */

read_var_var (string ZVP, string INDEX)
@@@
     PHC::write_var_var ($ZVP, $INDEX);
@@@

get_var_var (string ST, string ZVP, string INDEX)
@@@
     PHC::get_var_var ($ST,$ZVP,$INDEX);
@@@

assign_var_var (token INDEX, node RHS)
@@@
     PHC::assign_var_var ($INDEX,$RHS);
@@@

assign_var_var_ref (token INDEX, node RHS)
@@@
     PHC::assign_var_var_ref ($INDEX,$RHS);
@@@

/*
 * Assign_next
 * TODO: These are are more than a bit of a mess.
 */
assign_next (token LHS, token RHS)
@@@
     PHC::assign_next ($LHS,$RHS);
@@@

assign_next_ref (token LHS, node RHS)
@@@
     PHC::assign_next_ref ($LHS,$RHS);
@@@


/*
 * Assign_array
 */

assign_array (token ARRAY, token INDEX, token RHS)
@@@
     PHC::assign_array ($ARRAY,$INDEX,$RHS);
@@@

assign_array_ref (token ARRAY, node INDEX, token RHS)
@@@
     PHC::assign_array_ref ($ARRAY,$INDEX,$RHS);
@@@

/*
 * Globals
 */
global_var (token VAR)
@@@
     PHC::global_var (VAR);
@@@

global_var_var (token INDEX)
@@@
     PHC::global_var_var ($INDEX);
@@@

/*
 * Builtins
 */

builtin_with_lhs (token LHS, token ARG, string NAME, string FILENAME)
@@@
     PHC::builtin_with_lhs<$NAME> ($LHS, $ARG, $NAME,  "$FILENAME");
@@@


builtin_no_lhs (token ARG, string NAME, string FILENAME)
@@@
     PHC::builtin_no_lhs<$NAME> ($ARG, $NAME,  "$FILENAME");
@@@

/*
 * Foreach
 */

// TODO: Find a nice way to avoid this duplication
assign_expr_ref_foreach_get_val (token LHS, token ARRAY, string ITERATOR)
@@@
     PHC::assign_expr_ref_foreach_get_val ($LHS,$ARRAY,$ITERATOR);
@@@

assign_expr_foreach_get_val (token LHS, token ARRAY, string ITERATOR)
@@@
    PHC::assign_expr_foreach_get_val ( $LHS,  $ARRAY,  $ITERATOR);
@@@

assign_expr_foreach_has_key (token LHS, token ARRAY, string ITERATOR)
@@@
    PHC::assign_expr_foreach_has_key ( $LHS,  $ARRAY,  $ITERATOR);
@@@

assign_expr_foreach_get_key (token LHS, token ARRAY, string ITERATOR)
@@@
    PHC::assign_expr_foreach_get_key ( $LHS,  $ARRAY,  $ITERATOR);
@@@

foreach_reset (token ARRAY, string ITERATOR)
@@@
			     PHC::foreach_reset ( $ARRAY,  $ITERATOR);
@@@

foreach_next (token ARRAY, string ITERATOR)
@@@
			     PHC::foreach_next ( $ARRAY,  $ITERATOR);
@@@

foreach_end (token ARRAY, string ITERATOR)
@@@
			     PHC::foreach_end ( $ARRAY,  $ITERATOR);
@@@


/*
 * Assign_field 
 *
 */

assign_field (token OBJ, token FIELD, node RHS)
@@@
     PHC::assign_field ( $OBJ,  $FIELD,  $RHS);
@@@

assign_field_ref (token OBJ, token FIELD, node RHS)
@@@
     PHC::assign_field_ref ( $OBJ,  $FIELD,  $RHS);
@@@

assign_static_field (token CLASS, token FIELD, node RHS)
@@@
    PHC::assign_static_field ( $CLASS,  $FIELD,  $RHS);
@@@

assign_static_field_ref (token CLASS, token FIELD, node RHS)
@@@
     PHC::assign_static_field_ref ( $CLASS,  $FIELD,  $RHS);
@@@

assign_var_field (token OBJ, token VAR_FIELD, node RHS)
@@@
    PHC::assign_var_field ( $OBJ,  $VAR_FIELD,  $RHS);
@@@

assign_var_field_ref (token OBJ, token VAR_FIELD, node RHS)
@@@
    PHC::assign_var_field_ref ( $OBJ,  $VAR_FIELD,  $RHS);
@@@

assign_var_static_field (token CLASS, token VAR_FIELD, node RHS)
@@@
    PHC::assign_var_static_field ($CLASS,  $VAR_FIELD,  $RHS);
@@@

assign_var_static_field_ref (token CLASS, token VAR_FIELD, node RHS)
@@@
    PHC::assign_var_static_field_ref ( $CLASS, $VAR_FIELD, $RHS);
@@@

/*
 * Field_access
 */

// TODO: we use the C function write_var rather than the write_var macro,
// which is not general enough to handle this case 
field_access (token LHS, token OBJ, token FIELD)
@@@
    PHC::field_access ( LHS,  OBJ,  FIELD);
@@@

field_access_ref (token LHS, token OBJ, token FIELD)
@@@
     PHC::field_access_ref ( $LHS,  $OBJ,  $FIELD);;
@@@

static_field_access (token LHS, token CLASS, token FIELD)
@@@
    PHC::static_field_access ($LHS, $CLASS,  $FIELD);
@@@

static_field_access_ref (token LHS, token CLASS, token FIELD)
@@@
     PHC::static_field_access_ref ($LHS, $CLASS,  $FIELD);
@@@

var_field_access (token LHS, token OBJ, token VAR_FIELD)
@@@
     PHC::var_field_access ( $LHS,  $OBJ,  $VAR_FIELD);
@@@

var_field_access_ref (token LHS, token OBJ, token VAR_FIELD)
@@@
    PHC::var_field_access_ref ( $LHS,  $OBJ,  $VAR_FIELD);
@@@

var_static_field_access (token LHS, token CLASS, token VAR_FIELD)
@@@
    PHC::var_static_field_access ( $LHS,  $CLASS,  $VAR_FIELD);
@@@

var_static_field_access_ref (token LHS, token CLASS, token VAR_FIELD)
@@@
    PHC::var_static_field_access_ref ( $LHS,  $CLASS,  $VAR_FIELD);
@@@

/*
 * Object creation
 */

assign_expr_new (token LHS, token CLASS)
@@@
    PHC::assign_expr_new ($LHS,$CLASS);
@@@

assign_expr_new_ref (token LHS, token CLASS)
@@@
    PHC::assign_expr_new_ref ( $LHS,  $CLASS);
@@@

assign_expr_var_new (token LHS, token VAR_CLASS)
@@@
     PHC::assign_expr_var_new ( $LHS,  $VAR_CLASS);
@@@

assign_expr_var_new_ref (token LHS, token VAR_CLASS)
@@@
     PHC::assign_expr_var_new_ref ( $LHS, $VAR_CLASS);
@@@

/*
 * Lots of macros need to fetch the LHS, initialize/separate it, and add a
 * value. In Generate_C, this used to be Pattern_assign_var, but now they just
 * call this macro.
 */
new_lhs (token LHS, string VAL)
@@@
     PHC::new_lhs ( $LHS,  $VAL);
@@@

/*
 * Method and function calls
 */

arg_by_ref (node ARG)
   where ARG.is_ref
@@@
	 PHC::arg_by_ref ($ARG);
@@@


arg_by_ref (node ARG)
@@@
     PHC::arg_by_ref ($ARG);
@@@

// TODO: these can easily be combined
arg_fetch (token ARG)
@@@
	   PHC::arg_fetch ($ARG);
@@@

call_function (string MN, list ARGS, string FILENAME, string LINE, string FCI_NAME, string FCIC_NAME, string ARG_COUNT, string USE_REF, token LHS)
@@@
    PHC::call_function ( $MN,  $ARGS, "$FILENAME",  
			 $LINE,  $FCI_NAME,  $FCIC_NAME,  
			 $ARG_COUNT,  $USE_REF,  $LHS);
@@@


function_lhs (string USE_LHS, token LHS) where USE_LHS == "NONE" @@@@@@

function_lhs (string USE_LHS, token LHS)
   where USE_LHS == "REF"
@@@
  PHC::function_lhs ( $USE_LHS, $LHS);
@@@

function_lhs (string USE_LHS, token LHS)
   where USE_LHS == "NO_REF"
@@@
  PHC::function_lhs ($USE_LHS,  $LHS);
@@@

function_invocation (string MN, list ARGS, string FILENAME, string LINE, string FCI_NAME, string FCIC_NAME, string ARG_COUNT, string LHS_REF, token LHS)
@@@
     PHC::function_invocation ($MN, $ARGS, "$FILENAME", $LINE, $FCI_NAME, $FCIC_NAME, $ARG_COUNT, $LHS_REF, $LHS);
@@@

assign_param_is_ref (string MN, string FILENAME, string LINE, string FCI_NAME, string FCIC_NAME, string INDEX, token LHS)
@@@
  PHC::assign_param_is_ref ( $MN,"$FILENAME", $LINE, $FCI_NAME, $FCIC_NAME, $INDEX, 
$LHS);
@@@

method_invocation (string MN, list ARGS, string FILENAME, string LINE, string ARG_COUNT, node TARGET, string USE_REF, token LHS)
@@@
  PHC::method_invocation ($MN, $ARGS, "$FILENAME", $LINE, $ARG_COUNT, $TARGET, $USE_REF,$LHS);
@@@

constructor_invocation (list ARGS, string FILENAME, string LINE, string ARG_COUNT, token LHS)
@@@

  PHC::constructor_invocation ($ARGS, "$FILENAME", $LINE, $ARG_COUNT,$LHS);
@@@

make_field_name (string VAR_NAME, token FIELD)
@@@
  PHC::make_field_name ($VAR_NAME, $FIELD);
@@@
